/*
 * usart_rx.c
 *
 *  Created on: 2018��9��23��
 *      Author: Dandjinh
 */

#include "main.h"
#include "usart_rx.h"
#include "usart.h"
#include "cmsis_os.h"
#include "limits.h"
#include "eeprom.h"
#include "crc.h"
#include "usart_tx.h"

#define USART_RX_NOTIFY_RECEIVED 1
#define USART_RX_NOTIFY_FULL     2
#define USART_RX_NOTIFY_ERROR    -1

#define USART_RX_BUFFER_SIZE 280

#define CMD_RD_ING  1    // 正在读取
#define CMD_RD_DONE 2    // 读取完成
#define CMD_WR_ING  3    // 正在写入
#define CMD_WR_DONE 4    // 写入完成

#define CMD_NOSPACE 0x81 // 内存不足
#define CMD_RD_ERR  0x82 // 读取错误
#define CMD_WR_ERR  0x83 // 写入错误
#define CMD_CRC_ERR 0x84 // CRC校验失败

#pragma pack(1)
typedef struct {
  uint8_t mark[2];
  uint8_t cmd;
  uint8_t len;
} PacketHead_t;

typedef struct {
  uint16_t addr;
  uint16_t size;
  uint32_t crc;
} PacketWrite_t;

typedef struct {
  uint16_t beginAddr;
  uint16_t size;
} PacketRead_t;
#pragma pack()

static osThreadId usartRxTaskId = NULL;
static uint8_t usartRxBuffer[USART_RX_BUFFER_SIZE];

static void UsartRx_MainLoop(void const *param);
static void UsartRx_StartDmaRx(void);
static void UsartRx_StopDmaRx(void);
static void UsartRx_ReporCMD(uint8_t cmd);
static void UsartRx_ReadEeprom(uint16_t beginAddr, uint16_t size);
static void UsartRx_WriteEeprom(uint16_t addr, uint8_t *pData, uint16_t size, uint32_t crc);

static void UsartRx_NotifyReceivedCB(void);
static void UsartRx_NotifyFullCB(void);
static void UsartRx_NotifyErrorCB(void);

void UsartRx_CreateTask(void)
{
  if (usartRxTaskId == NULL) {
    osThreadDef(usartRxTask, UsartRx_MainLoop, osPriorityNormal, 0, 256);
    usartRxTaskId = osThreadCreate(osThread(usartRxTask), NULL);
  }
}

static void UsartRx_MainLoop(void const *param)
{
  uint32_t notifyValue;

  UsartRx_StartDmaRx();

  while (1) {
    xTaskNotifyWait(0, ULONG_MAX, &notifyValue, osWaitForever);
    switch (notifyValue) {
    case USART_RX_NOTIFY_RECEIVED:
      // 正常接收
      UsartRx_NotifyReceivedCB();
      break;

    case USART_RX_NOTIFY_FULL:
      // 缓冲区溢出
      UsartRx_NotifyFullCB();
      break;

    case USART_RX_NOTIFY_ERROR:
      // 串口错误
      UsartRx_NotifyErrorCB();
      break;
    }
  }
}

void UsartRx_IdleCallback(UART_HandleTypeDef *huart)
{
  if (huart->Instance == USART1) {
    if (__HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE) != RESET
        && __HAL_UART_GET_IT_SOURCE(huart, UART_IT_IDLE) != RESET) {
      BaseType_t xHigherPriorityTaskWoken = pdFALSE;

      __HAL_UART_DISABLE_IT(huart, UART_IT_IDLE);
      __HAL_UART_CLEAR_IDLEFLAG(huart);

      UsartRx_StopDmaRx();
      xTaskNotifyFromISR(usartRxTaskId, (uint32_t)USART_RX_NOTIFY_RECEIVED,
          eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
      portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    }
  }
}

void UsartRx_RxCpltCallback(UART_HandleTypeDef *huart)
{
  BaseType_t xHigherPriorityTaskWoken = pdFALSE;

  xTaskNotifyFromISR(usartRxTaskId, (uint32_t)USART_RX_NOTIFY_FULL,
      eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
  portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

void UsartRx_ErrorCallback(UART_HandleTypeDef *huart)
{
  BaseType_t xHigherPriorityTaskWoken = pdFALSE;

  xTaskNotifyFromISR(usartRxTaskId, (uint32_t)USART_RX_NOTIFY_ERROR,
      eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
  portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}

static void UsartRx_StartDmaRx(void)
{
  HAL_StatusTypeDef ret;
  uint8_t tryTimes = 0;

  ret = HAL_UART_Receive_DMA(&huart1, usartRxBuffer, USART_RX_BUFFER_SIZE);
  while (ret != HAL_OK && tryTimes++ < 3) {
    osDelay(100);
    ret = HAL_UART_Receive_DMA(&huart1, usartRxBuffer, USART_RX_BUFFER_SIZE);
  }

  if (ret == HAL_OK) {
    /* 开启串口空闲中断 */
    __HAL_UART_CLEAR_IDLEFLAG(&huart1);
    __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
  }
}

static void UsartRx_StopDmaRx(void)
{
  UART_HandleTypeDef *huart = &huart1;

  /* Stop UART DMA Rx request if ongoing */
  if ((huart->RxState == HAL_UART_STATE_BUSY_RX) &&
      (HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR)))
  {
    CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR);

    /* Abort the UART DMA Rx channel */
    if(huart->hdmarx != NULL)
    {
      HAL_DMA_Abort(huart->hdmarx);
    }

    /* Disable RXNE, PE and ERR (Frame error, noise error, overrun error) interrupts */
    CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE));
    CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);

    /* At end of Rx process, restore huart->RxState to Ready */
    huart->RxState = HAL_UART_STATE_READY;
  }
}

static void UsartRx_NotifyReceivedCB(void)
{
  PacketHead_t *pHead;
  uint8_t *pData;
  uint16_t len;

  len = USART_RX_BUFFER_SIZE - (uint16_t) (huart1.hdmarx->Instance->CNDTR & 0xFFFF);

  pHead = (PacketHead_t *)usartRxBuffer;
  if (pHead->mark[0] == 0xAB && pHead->mark[1] == 0xEF) {
    pData = usartRxBuffer + sizeof(PacketHead_t);
    if (pHead->cmd == 0xF && pHead->len == sizeof(PacketWrite_t)) {
      PacketWrite_t *pWrite = (PacketWrite_t *)pData;
      if (pWrite->size + sizeof(PacketHead_t) + sizeof(PacketWrite_t) <= len) {
        UsartRx_WriteEeprom(pWrite->addr, pData + sizeof(PacketWrite_t), pWrite->size, pWrite->crc);
      }
    } else if (pHead->cmd == 0xF0 && pHead->len >= sizeof(PacketRead_t *) && len >= (sizeof(PacketHead_t) + sizeof(PacketRead_t *))) {
      PacketRead_t *pRead = (PacketRead_t *)pData;
      UsartRx_ReadEeprom(pRead->beginAddr, pRead->size);
    }
  }

  UsartRx_StartDmaRx();
}

static void UsartRx_NotifyFullCB(void)
{
  UsartRx_NotifyReceivedCB();
}

static void UsartRx_NotifyErrorCB(void)
{
  UsartRx_StopDmaRx();
  UsartRx_StartDmaRx();
}

static void UsartRx_ReporCMD(uint8_t cmd)
{
  PacketHead_t *packet;

  packet = (PacketHead_t *) pvPortMalloc(sizeof(PacketHead_t));
  packet->mark[0] = 0xFE;
  packet->mark[1] = 0xBA;
  packet->cmd = cmd;
  packet->len = 0;

  UsartTx_SendData((uint8_t *)packet, sizeof(PacketHead_t), 1);
}

static void UsartRx_ReadEeprom(uint16_t beginAddr, uint16_t size)
{
  uint8_t *pData, *pRealData;
  PacketHead_t *pHead;
  PacketWrite_t *pWrite;
  HAL_StatusTypeDef ret;

  pData = (uint8_t *) pvPortMalloc(sizeof(PacketHead_t) + sizeof(PacketWrite_t) + size);
  if (pData == NULL) {
    UsartRx_ReporCMD(CMD_NOSPACE);
    return;
  }

  pRealData = pData + sizeof(PacketHead_t) + sizeof(PacketWrite_t);
  ret = EEPROM_ReadBytes(beginAddr, pRealData, size, 1000);
  if (ret != HAL_OK) {
    vPortFree(pData);
    UsartRx_ReporCMD(CMD_RD_ERR);
    return;
  }

  pHead = (PacketHead_t *)pData;
  pHead->mark[0] = 0xFE;
  pHead->mark[1] = 0xBA;
  pHead->cmd = CMD_RD_DONE;
  pHead->len = sizeof(PacketWrite_t);

  pWrite = (PacketWrite_t *)(pData + sizeof(PacketHead_t));
  pWrite->addr = beginAddr;
  pWrite->size = size;
  pRealData = pData + sizeof(PacketHead_t) + sizeof(PacketWrite_t);
  pWrite->crc = MY_Crc_Calc(pRealData, size);

  UsartTx_SendData(pData, sizeof(PacketHead_t) + sizeof(PacketWrite_t) + size, 1);
}

static void UsartRx_WriteEeprom(uint16_t addr, uint8_t *pData, uint16_t size, uint32_t crc)
{
  uint32_t _crc;
  HAL_StatusTypeDef ret;

  _crc = MY_Crc_Calc(pData, size);
  if (_crc != crc) {
    UsartRx_ReporCMD(CMD_CRC_ERR);
    return;
  }

  ret = EEPROM_WriteBytes(addr, pData, size, 1000);
  if (ret != HAL_OK) {
    UsartRx_ReporCMD(CMD_WR_ERR);
  } else {
    UsartRx_ReporCMD(CMD_WR_DONE);
  }
}
